package com.tracy.cprinter.printer;

import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.Message;

import androidx.annotation.Nullable;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.WritableArray;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.gprinter.io.PortManager;
import com.gprinter.io.UsbPort;

import java.io.IOException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import static android.hardware.usb.UsbManager.ACTION_USB_DEVICE_ATTACHED;
import static android.hardware.usb.UsbManager.ACTION_USB_DEVICE_DETACHED;

public class UsbDeviceConnectManager {
    private static UsbDeviceConnectManager instance;
    private ReactApplicationContext reactContext;
    public PortManager mPort;
    public boolean isOpenPort;
    public PrinterReader reader;

    public String encode = "GB2312";

    /**
     * ESC查询打印机实时状态指令
     */
    private byte[] esc = {0x10, 0x04, 0x02};
    /**
     * TSC查询打印机状态指令
     */
    private byte[] tsc = {0x1b, '!', '?'};
    private byte[] cpcl = {0x1b, 0x68};
    private byte[] sendCommand;
    /**
     * 判断打印机所使用指令是否是ESC指令
     */
    private PrinterCommand currentPrinterCommand;

    public static final String ACTION_USB_PERMISSION = "com.android.example.USB_PERMISSION";
    public static final String EVENT_PRINTER_CONNECT = "EVENT_PRINTER_CONNECT";
    public static final String EVENT_PRINTER_STATUS = "EVENT_PRINTER_STATUS";

    private static final int READ_DATA = 10000;
    private static final String READ_DATA_CNT = "read_data_cnt";
    private static final String READ_BUFFER_ARRAY = "read_buffer_array";

    public static boolean whichFlag = true;
    /**
     * ESC查询打印机实时状态 缺纸状态
     */
    private static final int ESC_STATE_PAPER_ERR = 0x20;
    /**
     * ESC指令查询打印机实时状态 打印机开盖状态
     */
    private static final int ESC_STATE_COVER_OPEN = 0x04;
    /**
     * ESC指令查询打印机实时状态 打印机报错状态
     */
    private static final int ESC_STATE_ERR_OCCURS = 0x40;
    /**
     * ESC低电量
     */
    private static final int ESC_LOW_POWER = 0x31;
    /**
     * ESC中电量
     */
    private static final int ESC_AMID_POWER = 0x32;
    /**
     * ESC高电量
     */
    private static final int ESC_HIGH_POWER = 0x33;
    /**
     * ESC正在充电
     */
    private static final int ESC_CHARGING = 0x35;
    /**
     * TSC指令查询打印机实时状态 打印机缺纸状态
     */
    private static final int TSC_STATE_PAPER_ERR = 0x04;
    /**
     * TSC指令查询打印机实时状态 打印机开盖状态
     */
    private static final int TSC_STATE_COVER_OPEN = 0x01;
    /**
     * TSC指令查询打印机实时状态 打印机出错状态
     */
    private static final int TSC_STATE_ERR_OCCURS = 0x80;
    /**
     * CPCL指令查询打印机实时状态 打印机缺纸状态
     */
    private static final int CPCL_STATE_PAPER_ERR = 0x01;
    /**
     * CPCL指令查询打印机实时状态 打印机开盖状态
     */
    private static final int CPCL_STATE_COVER_OPEN = 0x02;
    public static final byte FLAG = 0x10;

    /**
     * 单例
     */
    public static synchronized UsbDeviceConnectManager getInstance() {
        if (instance == null) {
            instance = new UsbDeviceConnectManager();
        }

        return instance;
    }

    /**
     * 发送事件
     */
    public void sendEvent(String eventName, @Nullable WritableMap params) {
        reactContext
                .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
                .emit(eventName, params);
    }

    public void setReactContext(ReactApplicationContext context) {
        reactContext = context;
        IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
        filter.addAction(ACTION_USB_DEVICE_DETACHED);
        filter.addAction(ACTION_USB_DEVICE_ATTACHED);
        reactContext.registerReceiver(mBroadcastReceiver, filter);
    }

    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            String action = intent.getAction();
            switch (action) {
                case ACTION_USB_PERMISSION:
                    synchronized (this) {
                        UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
                        if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
                            if (device != null) {
                                usbDeviceConnectRequest(device);
                            } else {
                                WritableMap map = Arguments.createMap();
                                map.putInt("code", 2001);
                                map.putString("msg", "usb权限请求失败，设备不存在");
                                map.putString("status", "CONN_STATE_FAILED");
                                sendEvent(EVENT_PRINTER_CONNECT, map);
                                isOpenPort = false;
                            }
                        } else {
                            WritableMap map = Arguments.createMap();
                            map.putInt("code", 2001);
                            map.putString("msg", "usb权限请求失败");
                            map.putString("status", "CONN_STATE_FAILED");
                            sendEvent(EVENT_PRINTER_CONNECT, map);
                            isOpenPort = false;
                        }
                    }
                    break;
                /* Usb连接断开、蓝牙连接断开广播 */
                case ACTION_USB_DEVICE_DETACHED:
                    stopConnect();
                    WritableMap map = Arguments.createMap();
                    map.putInt("code", 3001);
                    map.putString("msg", "Usb连接断开");
                    map.putString("status", "CONN_STATE_DISCONNECT");
                    sendEvent(EVENT_PRINTER_CONNECT, map);
                    break;
                default:
                    break;
            }
        }
    };

    /**
     * 获取usb设备列表
     */
    public WritableArray getUsbDeviceList() {
        UsbManager manager = (UsbManager) reactContext.getSystemService(Context.USB_SERVICE);
        // Get the list of attached devices
        HashMap<String, UsbDevice> devices = manager.getDeviceList();
        Iterator<UsbDevice> deviceIterator = devices.values().iterator();
        int count = devices.size();
        WritableArray list = Arguments.createArray();
        if (count > 0) {
            while (deviceIterator.hasNext()) {
                UsbDevice device = deviceIterator.next();
                String devicename = device.getDeviceName();
                if (checkUsbDevicePidVid(device)) {
                    list.pushString(devicename);
                }
            }
        }

        return list;
    }

    public boolean checkUsbDevicePidVid(UsbDevice dev) {
        int pid = dev.getProductId();
        int vid = dev.getVendorId();
        return ((vid == 34918 && pid == 256) || (vid == 1137 && pid == 85)
                || (vid == 6790 && pid == 30084)
                || (vid == 26728 && pid == 256) || (vid == 26728 && pid == 512)
                || (vid == 26728 && pid == 256) || (vid == 26728 && pid == 768)
                || (vid == 26728 && pid == 1024) || (vid == 26728 && pid == 1280)
                || (vid == 26728 && pid == 1536));
    }

    /**
     * 根据usb设备名称获取设备数据
     */
    public UsbDevice getUsbDeviceFromName(String usbName) {
        UsbManager usbManager = (UsbManager) reactContext.getSystemService(Context.USB_SERVICE);
        HashMap<String, UsbDevice> usbDeviceList = usbManager.getDeviceList();
        return usbDeviceList.get(usbName);
    }

    /**
     * usb设备连接
     */
    public void usbDeviceConnect(String usbName) {
        stopConnect();
        /* 通过USB设备名找到USB设备 */
        UsbDevice device = getUsbDeviceFromName(usbName);
        /* 判断USB设备是否有权限 */
        UsbManager usbManager = (UsbManager) reactContext.getSystemService(Context.USB_SERVICE);
        if (usbManager.hasPermission(device)) {
            usbDeviceConnectRequest(device);
        } else {
            // 请求权限
            PendingIntent mPermissionIntent = PendingIntent.getBroadcast(reactContext, 0, new Intent(ACTION_USB_PERMISSION), 0);
            usbManager.requestPermission(device, mPermissionIntent);
        }
    }

    /**
     * usb设备连接请求
     */
    public void usbDeviceConnectRequest(UsbDevice device) {
        stopConnect();
        WritableMap map = Arguments.createMap();
        map.putInt("code", 0);
        map.putString("msg", "Usb连接中");
        map.putString("status", "CONN_STATE_CONNECTING");
        sendEvent(EVENT_PRINTER_CONNECT, map);

        mPort = new UsbPort(reactContext, device);
        isOpenPort = mPort.openPort();
        if (isOpenPort) {
            queryCommand();
        } else {
            if (mPort != null) {
                mPort = null;
            }
            WritableMap data = Arguments.createMap();
            data.putInt("code", 3001);
            data.putString("msg", "Usb连接失败");
            data.putString("status", "CONN_STATE_FAILED");
            sendEvent(EVENT_PRINTER_CONNECT, data);
        }
    }

    /**
     * 停止连接
     */
    public void stopConnect() {
        if (mPort != null) {
            mPort = null;
            isOpenPort = false;
            currentPrinterCommand = null;
            if (reader != null) {
                reader.cancel();
            }
            reader = null;
        }
    }

    /**
     * 停止连接并发送事件
     */
    public void stopConnectWithEvent() {
        stopConnect();
        WritableMap map = Arguments.createMap();
        map.putInt("code", 3001);
        map.putString("msg", "Usb连接断开");
        map.putString("status", "CONN_STATE_DISCONNECT");
        sendEvent(EVENT_PRINTER_CONNECT, map);
    }

    /**
     * 查询当前连接打印机所使用打印机指令（ESC（EscCommand.java）、TSC（LabelCommand.java））
     */
    private void queryCommand() {
        //开启读取打印机返回数据线程
        reader = new PrinterReader();
        reader.start(); //读取数据线程
        //查询打印机所使用指令
        queryPrinterCommand(); //
    }

    /**
     * 查询打印机当前使用的指令（TSC、ESC）
     */
    private void queryPrinterCommand() {
        //线程池添加任务
        ThreadPool.getInstantiation().addTask(new Runnable() {
            @Override
            public void run() {
                //发送ESC查询打印机状态指令
                sendCommand = esc;
                Vector<Byte> data = new Vector<>(esc.length);
                for (int i = 0; i < esc.length; i++) {
                    data.add(esc[i]);
                }
                sendDataImmediately(data); //发送esc数据
                //开启计时器，隔2000毫秒没有没返回值时发送TSC查询打印机状态指令
                final ThreadFactoryBuilder threadFactoryBuilder = new ThreadFactoryBuilder("Timer");
                final ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(1, threadFactoryBuilder);
                scheduledExecutorService.schedule(threadFactoryBuilder.newThread(new Runnable() {
                    @Override
                    public void run() {
                        if (currentPrinterCommand == null || currentPrinterCommand != PrinterCommand.ESC) {
//                            Log.e(TAG, Thread.currentThread().getName());
                            //发送TSC查询打印机状态指令
                            sendCommand = tsc;
                            Vector<Byte> data = new Vector<>(tsc.length);
                            for (int i = 0; i < tsc.length; i++) {
                                data.add(tsc[i]);
                            }
                            sendDataImmediately(data);
                            //开启计时器，隔2000毫秒没有没返回值时发送CPCL查询打印机状态指令
                            scheduledExecutorService.schedule(threadFactoryBuilder.newThread(new Runnable() {
                                @Override
                                public void run() {
                                    if (currentPrinterCommand == null || (currentPrinterCommand != PrinterCommand.ESC && currentPrinterCommand != PrinterCommand.TSC)) {
//                                        Log.e(TAG, Thread.currentThread().getName());
                                        //发送CPCL查询打印机状态指令
                                        sendCommand = cpcl;
                                        Vector<Byte> data = new Vector<Byte>(cpcl.length);
                                        for (int i = 0; i < cpcl.length; i++) {
                                            data.add(cpcl[i]);
                                        }
                                        sendDataImmediately(data);
                                        //开启计时器，隔2000毫秒打印机没有响应者停止读取打印机数据线程并且关闭端口
                                        scheduledExecutorService.schedule(threadFactoryBuilder.newThread(new Runnable() {
                                            @Override
                                            public void run() {
                                                if (currentPrinterCommand == null) {
                                                    if (reader != null) {
                                                        reader.cancel();
                                                        mPort.closePort();
                                                        isOpenPort = false;
                                                        mPort = null;
                                                        WritableMap map = Arguments.createMap();
                                                        map.putInt("code", 3001);
                                                        map.putString("msg", "Usb连接失败");
                                                        map.putString("status", "CONN_STATE_FAILED");
                                                        sendEvent(EVENT_PRINTER_CONNECT, map);
                                                    }
                                                }
                                            }
                                        }), 2000, TimeUnit.MILLISECONDS);
                                    }
                                }
                            }), 2000, TimeUnit.MILLISECONDS);
                        }
                    }
                }), 2000, TimeUnit.MILLISECONDS);
            }
        });
    }

    public void sendDataImmediately(final Vector<Byte> data) {
        if (this.mPort == null) {
            return;
        }
        try {
            //  Log.e(TAG, "data -> " + new String(com.gprinter.command.GpUtils.convertVectorByteTobytes(data), "gb2312"));
            this.mPort.writeDataImmediately(data, 0, data.size());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    class PrinterReader extends Thread {
        private boolean isRun = false;

        private byte[] buffer = new byte[100];

        public PrinterReader() {
            isRun = true;
        }

        @Override
        public void run() {
            try {
                while (isRun) {
                    //读取打印机返回信息
                    int len = readDataImmediately(buffer);
                    if (len > 0) {
                        Message message = Message.obtain();
                        message.what = READ_DATA;
                        Bundle bundle = new Bundle();
                        bundle.putInt(READ_DATA_CNT, len); //数据长度
                        bundle.putByteArray(READ_BUFFER_ARRAY, buffer); //数据
                        message.setData(bundle);
                        handleMessage(message);
                    }
                }
            } catch (Exception e) {
                stopConnectWithEvent();
            }
        }

        public void cancel() {
            isRun = false;
        }
    }

    public int readDataImmediately(byte[] buffer) throws IOException {
        return this.mPort.readData(buffer);
    }

    /**
     * 判断是实时状态（10 04 02）还是查询状态（1D 72 01）
     */
    private int judgeResponseType(byte r) {
        return (byte) ((r & FLAG) >> 4);
    }

    private void handleMessage(Message msg) {
        switch (msg.what) {
            case READ_DATA:
                int cnt = msg.getData().getInt(READ_DATA_CNT); //数据长度 >0;
                byte[] buffer = msg.getData().getByteArray(READ_BUFFER_ARRAY);  //数据
                //这里只对查询状态返回值做处理，其它返回值可参考编程手册来解析
                if (buffer == null) {
                    return;
                }
                int result = judgeResponseType(buffer[0]); //数据右移
                String status = "打印机连接正常";
                WritableMap eventMap = Arguments.createMap();
                eventMap.putBoolean("is_connected", true);
                eventMap.putInt("result", result);
                eventMap.putInt("cnt", cnt);
                if (sendCommand == esc) {
                    //设置当前打印机模式为ESC模式
                    if (currentPrinterCommand == null) {
                        currentPrinterCommand = PrinterCommand.ESC;
                        WritableMap map = Arguments.createMap();
                        map.putString("status", "CONN_STATE_CONNECTED");
                        map.putString("msg", "打印机连接成功");
                        sendEvent(EVENT_PRINTER_CONNECT, map);
                    } else {//查询打印机状态
                        if (result == 1) {//查询打印机实时状态
                            boolean isError = false;
                            if (whichFlag) {
                                if ((buffer[0] & ESC_STATE_PAPER_ERR) > 0) {
                                    status += " 打印机缺纸";
                                    isError = true;
                                }
                                if ((buffer[0] & ESC_STATE_COVER_OPEN) > 0) {
                                    status += " 打印机开盖";
                                    isError = true;
                                }
                                if ((buffer[0] & ESC_STATE_ERR_OCCURS) > 0) {
                                    status += " 打印机出错";
                                    isError = true;
                                }
                            } else {
                                if ((buffer[0] == ESC_LOW_POWER)) {
                                    status += " 电量：低电量";
                                } else if ((buffer[0] == ESC_AMID_POWER)) {
                                    status += " 电量：中电量";
                                } else if ((buffer[0] == ESC_HIGH_POWER)) {
                                    status += " 电量：高电量";
                                } else if ((buffer[0] == ESC_CHARGING)) {
                                    status = " 正在充电";
                                }
                            }
                            eventMap.putBoolean("is_error", isError);
                        }
                        eventMap.putString("status", status);
                        eventMap.putString("mode", "ESC");
                        sendEvent(EVENT_PRINTER_STATUS, eventMap);
                    }
                } else if (sendCommand == tsc) {
                    //设置当前打印机模式为TSC模式
                    if (currentPrinterCommand == null) {
                        currentPrinterCommand = PrinterCommand.TSC;
//                            sendStateBroadcast(CONN_STATE_CONNECTED);
                        WritableMap map = Arguments.createMap();
                        map.putString("status", "CONN_STATE_CONNECTED");
                        map.putString("msg", "打印机连接成功");
                        sendEvent(EVENT_PRINTER_CONNECT, map);
                    } else {
                        boolean isError = false;
                        if (cnt == 1) {//查询打印机实时状态
                            if ((buffer[0] & TSC_STATE_PAPER_ERR) > 0) {//缺纸
                                status += " 打印机缺纸";
                                isError = true;
                            }
                            if ((buffer[0] & TSC_STATE_COVER_OPEN) > 0) {//开盖
                                status += " 打印机开盖";
                                isError = true;
                            }
                            if ((buffer[0] & TSC_STATE_ERR_OCCURS) > 0) {//打印机报错
                                status += " 打印机出错";
                                isError = true;
                            }
                            eventMap.putBoolean("is_error", isError);
                        }
                        eventMap.putString("status", status);
                        eventMap.putString("mode", "TSC");
                        sendEvent(EVENT_PRINTER_STATUS, eventMap);
                    }
                } else if (sendCommand == cpcl) {
                    if (currentPrinterCommand == null) {
                        currentPrinterCommand = PrinterCommand.CPCL;
//                            sendStateBroadcast(CONN_STATE_CONNECTED);
                        WritableMap map = Arguments.createMap();
                        map.putString("status", "CONN_STATE_CONNECTED");
                        map.putString("msg", "打印机连接成功");
                        sendEvent(EVENT_PRINTER_CONNECT, map);
                    } else {
                        if (cnt == 1) {
                            boolean isError = false;
                            if ((buffer[0] == CPCL_STATE_PAPER_ERR)) {//缺纸
                                status += " " + "打印机缺纸";
                                isError = true;
                            }
                            if ((buffer[0] == CPCL_STATE_COVER_OPEN)) {//开盖
                                status += " " + "打印机开盖";
                                isError = true;
                            }

                            eventMap.putBoolean("is_error", isError);

                        }
                        eventMap.putString("status", status);
                        eventMap.putString("mode", "CPCL");
                        sendEvent(EVENT_PRINTER_STATUS, eventMap);
                    }
                }
                break;
            default:
                break;
        }
    }
}
